package gov.nist.healthcare.testframework.communication.server;

import gov.nist.healthcare.core.message.HL7Message;
import gov.nist.healthcare.core.message.Name;
import gov.nist.healthcare.testframework.Actor;
import gov.nist.healthcare.testframework.Environment;
import gov.nist.healthcare.testframework.application.TestAgent;
import gov.nist.healthcare.testframework.communication.CommunicationUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import org.apache.log4j.Logger;

public abstract class ServerThread extends Thread {

    private static final Logger logger = Logger.getLogger(ServerThread.class.getName());
    protected Socket socket = null;
    protected Name sending = null;

    public void setSocket(Socket socket) {
        this.socket = socket;
    }

    @Override
    public final void run() {
        logger.info("Connection accepted");
        // Get the message
        HL7Message message = null;
        message = getMessage();
        if (message != null) {
            Environment.getInstance().getEventModel().incomingMessageTestFramework(
                    message.getSending(), message.getReceiving(), message);
            // Pass the message to the test agent
            Name receiving = message.getReceiving();
            sending = message.getSending();
            TestAgent receivingTestAgent = Environment.getInstance().getTestAgent(
                    receiving);
            if (receivingTestAgent != null) {
                List<HL7Message> messages = receivingTestAgent.newMessage(message);
                logger.info(String.format(
                        "Message received from %s and forwarded to %s",
                        socket.getRemoteSocketAddress(), receiving));
                logger.info("\n" + message.getMessageAsString());

                if (messages != null && messages.size() > 0) {
                    try {
                        send(messages);
                    } catch (IOException ioe) {
                        logger.debug("Can't send the messages", ioe);
                    }
                }
            } else {
                logger.debug(String.format(
                        "Can't get the receiving test agent: %s", receiving));
            }
        }
        System.out.println("End of the server thread");
        try {
            socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Send the messages produced by the newMessage method.
     * 
     * @param messages
     *        a list of messages to send
     * @throws IOException
     */
    private void send(List<HL7Message> messages) throws IOException {
        for (HL7Message message : messages) {
            Name destination = message.getReceiving();
            // Use the current Socket if the message is a response
            if (destination.equals(sending)) {
                CommunicationUtils.send(socket, encode(message));
                Environment.getInstance().getEventModel().outgoingMessageTestFramework(
                        message.getSending(), message.getReceiving(), message);
            } else {
                Actor actor = Environment.getInstance().getActor(destination,
                        sending);
                boolean secure = getSecure();
                InetSocketAddress ip;
                if (secure) {
                    ip = actor.getSecureInetSocketAddress();
                } else {
                    ip = actor.getOpenInetSocketAddress();
                }
                if (ip != null) {
                    socket = CommunicationUtils.createSocket(ip, secure);
                    CommunicationUtils.send(socket, encode(message));
                    Environment.getInstance().getEventModel().outgoingMessageTestFramework(
                            message.getSending(), message.getReceiving(),
                            message);
                }
            }
        }
    }

    /**
     * Process the data that are receiving by using the InputStream from the
     * Socket.
     * 
     * @return an HL7Message object
     */
    public abstract HL7Message getMessage();

    /**
     * Encode the message before sending the data.
     * 
     * @param message
     * @return the encoded message based on the protocol
     */
    public abstract String encode(HL7Message message);

    /**
     * Return true if it's a secure listener.
     * 
     * @return true if it's a secure listener; false otherwise
     */
    public abstract boolean getSecure();

    /**
     * Restart the current Thread by creating a new one.
     * 
     * @return a new instance of ServerThread
     */
    public abstract ServerThread restart();

}
